package vars

import (
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/common"
	"gitee.com/gitee-go/core/model/pipeline"
	"gitee.com/gitee-go/core/models"
	"gitee.com/gitee-go/core/runtime"
	"gitee.com/gitee-go/server/comm"
	"gopkg.in/yaml.v2"
	"regexp"
	"runtime/debug"
	"strings"
)

func YMLReplaceVar(y *models.YML, repoId string) error {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("YMLReplaceVar process: %v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
		}
	}()
	//不能使用interface{}类型，必须要转一下指定类型
	err := replaceSYSVariables(y.Pie)
	if err != nil {
		return errors.New("系统变量替换失败")
	}
	//ReplaceVariables
	err = replaceVariables(y.Pie, repoId)
	if err != nil {
		return err
	}
	return nil
}

func replaceSYSVariables(pie *models.Pipeline) error {
	stages := pie.Stages
	err := replaceStages(stages)
	if err != nil {
		core.Log.Errorf("replaceSYSVariables replaceStages err:%v", err)
		return err
	}

	for _, stage := range stages {
		jobs := stage.Jobs
		err = replaceJobs(jobs)
		if err != nil {
			core.Log.Errorf("replaceSYSVariables replaceJobs err:%v", err)
			return err
		}
	}

	return nil
}

// 替换stage内的变量引入${{}}
func replaceStages(stages []*models.Stage) error {
	for _, stage := range stages {
		stageJs, err := json.Marshal(stage)
		if err != nil {
			core.Log.Errorf("ReplaceVariables err:%v", err)
			return err
		}
		vs := string(stageJs)
		if common.RegVar.Match(stageJs) {
			all := common.RegVar.FindAllStringSubmatch(string(stageJs), -1)
			for _, v := range all {
				if strings.Contains(strings.ToLower(v[0]), common.SYSEnv) {
					switch strings.ToLower(v[1]) {
					case common.SYSEnv + common.SYSCurrentStage + "." + common.SYSName:
						vs = strings.ReplaceAll(vs, v[0], stage.Name)
					case common.SYSEnv + common.SYSCurrentStage + "." + common.SYSDisplayName:
						vs = strings.ReplaceAll(vs, v[0], stage.DisplayName)
					}

				}
			}

			err = json.Unmarshal([]byte(vs), stage)
			if err != nil {
				core.Log.Errorf("ReplaceVariables err:%v", err)
				return err
			}

		}
	}

	return nil
}

func replaceJobs(jobs []*models.Job) error {
	for _, job := range jobs {
		jobJs, err := json.Marshal(job)
		if err != nil {
			core.Log.Errorf("ReplaceVariables err:%v", err)
			return err
		}

		vs := string(jobJs)
		if common.RegVar.Match(jobJs) {
			all := common.RegVar.FindAllStringSubmatch(string(jobJs), -1)
			for _, v := range all {

				if strings.Contains(strings.ToLower(v[0]), common.SYSEnv) {
					switch strings.ToLower(v[1]) {
					case common.SYSEnv + common.SYSCurrentJob + "." + common.SYSName:
						vs = strings.ReplaceAll(vs, v[0], strings.ReplaceAll(strings.ReplaceAll(job.Name, "\\", "\\\\"), "\"", "\\\""))
					case common.SYSEnv + common.SYSCurrentJob + "." + common.SYSDisplayName:
						vs = strings.ReplaceAll(vs, v[0], strings.ReplaceAll(strings.ReplaceAll(job.DisplayName, "\\", "\\\\"), "\"", "\\\""))
					}

				}
			}
			err = json.Unmarshal([]byte(vs), job)
			if err != nil {
				core.Log.Errorf("ReplaceVariables err:%v", err)
				return err
			}
		}
	}

	return nil
}
func replaceTSysVariable(s, old, name string) (string, error) {
	db := comm.DBMain.GetDB()
	sysVar := &pipeline.TSysVariable{}
	ok, err := db.Cols("name,value").Where("name=?", name).Get(sysVar)
	if err != nil {
		core.Log.Errorf("ReplaceVariables err:%v", err)
		return "", err
	}
	if !ok {
		return s, nil
	}
	return strings.ReplaceAll(s, old, sysVar.Value), nil
}

/*
replaceVariables Substitution variable like ${{var}}
要在yaml中 variables 字段声明的变量在下文中才可以使用
*/

func replaceVariables(pipe *models.Pipeline, repoId string) error {
	var tVars []*pipeline.TRepoVariable
	err := comm.DBMain.GetDB().Where("repo_id = ? ", repoId).Find(&tVars)
	if err != nil {
		return errors.New("未找到对应环境变量")
	}
	vms := make(map[string]*runtime.Variables, 0)
	for _, v := range tVars {
		vms[v.Name] = &runtime.Variables{
			Name:   v.Name,
			Value:  v.Value,
			Secret: v.Public != 0,
		}
	}
	rvms := make(map[string]*runtime.Variables, 0)
	for k, v := range pipe.Variables {
		k1, kok := replaceVariable(common.RegVar, k, vms)
		v1, vok := replaceVariable(common.RegVar, v, vms)
		rvms[k1] = &runtime.Variables{
			Name:   k1,
			Value:  v1,
			Secret: kok || vok,
		}
	}
	pipe.Vars = rvms
	return replaceStagesVar(pipe.Stages, rvms)
}

func replaceVariable(reg *regexp.Regexp, s string, vms map[string]*runtime.Variables) (string, bool) {
	if s == "" {
		return s, false
	}
	isSecret := false
	if reg.MatchString(s) {
		all := reg.FindAllStringSubmatch(s, -1)
		for _, v := range all {
			tVars, ok := vms[v[1]]
			if !ok {
				continue
			}
			if tVars.Secret {
				isSecret = true
			}
			s = strings.ReplaceAll(s, v[0], tVars.Value)
		}
	}
	return s, isSecret
}

func replaceStagesVar(stages []*models.Stage, mVars map[string]*runtime.Variables) error {
	for _, stage := range stages {
		return replaceStageVar(stage, mVars)
	}
	return nil
}
func replaceStageVar(stage *models.Stage, mVars map[string]*runtime.Variables) error {
	var err error
	stage.Stage, err = replace(stage.Stage, mVars)
	if err != nil {
		return err
	}
	stage.Name, err = replace(stage.Name, mVars)
	if err != nil {
		return err
	}
	stage.DisplayName, err = replace(stage.DisplayName, mVars)
	if err != nil {
		return err
	}
	if stage.Jobs != nil && len(stage.Jobs) > 0 {
		err = replaceSteps(stage.Jobs, mVars)
		if err != nil {
			return err
		}
	}
	return nil
}
func replaceSteps(steps []*models.Job, mVars map[string]*runtime.Variables) error {
	for _, step := range steps {
		err := replaceStep(step, mVars)
		if err != nil {
			return err
		}
	}
	return nil
}
func replaceStep(step *models.Job, mVars map[string]*runtime.Variables) error {
	var err error
	step.Job, err = replace(step.Job, mVars)
	if err != nil {
		return err
	}
	step.Name, err = replace(step.Name, mVars)
	if err != nil {
		return err
	}
	step.DisplayName, err = replace(step.DisplayName, mVars)
	if err != nil {
		return err
	}
	step.Image, err = replace(step.Image, mVars)
	if err != nil {
		return err
	}
	if step.Environments != nil && len(step.Environments) > 0 {
		step.Environments, err = replaceEnvs(step.Environments, mVars)
		if err != nil {
			return err
		}
	}
	return nil
}

func replaceEnvs(envs map[string]string, mVars map[string]*runtime.Variables) (map[string]string, error) {
	m := map[string]string{}
	for k, v := range envs {
		k1, err := replace(k, mVars, true)
		if err != nil {
			return nil, err
		}
		v1, err := replace(v, mVars, true)
		if err != nil {
			return nil, err
		}
		m[k1] = v1
	}
	return m, nil
}

func replace(s string, mVars map[string]*runtime.Variables, allowSecret ...bool) (string, error) {
	if s == "" {
		return s, nil
	}
	as := false
	if len(allowSecret) == 1 && allowSecret[0] {
		as = true
	}
	if common.RegVar.MatchString(s) {
		all := common.RegVar.FindAllStringSubmatch(s, -1)
		for _, v2 := range all {
			rVar, ok := mVars[v2[1]]
			if !ok {
				continue
			}
			if rVar.Secret && as {
				s = strings.ReplaceAll(s, v2[0], rVar.Value)
			} else if rVar.Secret && !as {
				return s, fmt.Errorf("引用了私密变量:%s", v2[0])
			} else {
				s = strings.ReplaceAll(s, v2[0], rVar.Value)
			}
		}
	}
	return s, nil
}

func CommandsToString(commands interface{}) (js string, reterr error) {
	if commands == nil {
		return "", nil
	}
	marshal, err := yaml.Marshal(commands)
	if err != nil {
		return "", err
	}
	return string(marshal), nil
}
